package com.sicnustray.realm;

import com.sicnustray.entity.Menu;
import com.sicnustray.jwt.JwtToken;
import com.sicnustray.jwt.JwtUtil;
import com.sicnustray.entity.User;
import com.sicnustray.service.IUserService;
import com.sicnustray.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * UserRealm:自定义一个授权
 *
 */

@Component
@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private IUserService iUserService;
    @Autowired
    private RedisUtils redisUtils;

    /**
     * 必须重写此方法，不然Shiro会报错
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = JwtUtil.getUsername(principals.toString());
        User user = iUserService.getByUsername(username);
        List<String> permissions=new ArrayList<>();
        //查询用户角色
        List<String> roles = Collections.singletonList(user.getRole());
        System.out.println("-------------当前用户的角色信息为："+roles);
        //查询用户权限菜单
        List<Menu> menus=iUserService.getRoleMenus(user.getRole());
        for (Menu menu:menus)
        {
            permissions.add(menu.getName());
            for (Menu child:menu.getChildren())
            {
                permissions.add(child.getName());
            }
        }
//        menus.forEach(item->permissions.add(item.getName()));
//        menus.forEach(item->permissions.add(item.getChildren()));
        System.out.println("-------------当前用户的权限菜单为："+menus);
//        创建对象封装对象信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;




    }

    /**
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     * @param auth
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String accessToken = (String) auth.getCredentials();
        // 解密获得username，用于和数据库进行对比
        String username = null;
        try {
            //处理空指针等异常
            username = JwtUtil.getUsername(accessToken);
        } catch (Exception e) {
            throw new AuthenticationException("heard的token拼写错误或者值为空");
        }
        if (username == null) {
            log.error("token无效(空''或者null都不行!)");
            throw new AuthenticationException("token无效");
        }
        User userBean = iUserService.getByUsername(username);
        if (userBean == null) {
            log.error("用户不存在!)");
            throw new AuthenticationException("用户不存在!");
        }
        if (!JwtUtil.verify(accessToken, username, userBean.getPassword())) {
            log.error("用户名或密码错误(token无效或者与登录者不匹配)!)");
            throw new AuthenticationException("用户名或密码错误(token无效或者与登录者不匹配)!");
        }

        //验证jwt中的accessToken
//        String key =(String) auth.getPrincipal();
        System.out.println("剩余时间为："+redisUtils.getExpire(username, TimeUnit.HOURS));
        if (JwtUtil.isTokenExpired(accessToken)){
            //验证redis中的freshToken
            if (redisUtils.getExpire(username)==null||redisUtils.getExpire(username)<0) {
                //如果freshToken过期则需要重新登录获取新的freshToken
                throw new AuthenticationException("token无效或已过期(有可能是刚刚更改了密码导致token失效)，请重新登录!");
            }
            else{
                //如果freshToken未过期而accessToken过期则重新生成accessToken
                JwtToken.newToken = JwtUtil.sign(username, userBean.getPassword());
                throw new AuthenticationException("token已过期，请使用新的token进行访问！");
            }
        }
        //jwt验证登录
        if (!JwtUtil.verify(accessToken, username, userBean.getPassword())) {
            log.error("用户名或密码错误(token无效或者与登录者不匹配)!");
            throw new AuthenticationException("用户名或密码错误(token无效或者与登录者不匹配)!");
        }

        return new SimpleAuthenticationInfo(accessToken, accessToken, "user_realm");
    }
}
